package com.bizmda.bizsip.converter;

import cn.hutool.core.util.CharsetUtil;
import cn.hutool.core.util.HexUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONObject;
import com.bizmda.bizsip.common.BizException;
import com.bizmda.bizsip.common.BizResultEnum;
import com.bizmda.bizsip.converter.iso8583.ByteBitUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;

import java.io.UnsupportedEncodingException;
import java.nio.charset.StandardCharsets;
import java.util.*;


/**
 * @author 史正烨
 */
@Slf4j
public class ISO8583Converter extends AbstractConverter {
    private Map<Integer, Map> fieldIndexMap;
    private Map<String, Map> fieldNameMap;
    private String destinationId;
    private String sourceId;

    @Override
    public void init(String configPath, Map messageMap) throws BizException {
        super.init(configPath, messageMap);
        this.destinationId = (String) messageMap.get("destination-id");
        this.destinationId = StrUtil.fillAfter(this.destinationId.trim(), ' ', 11);
        this.sourceId = (String) messageMap.get("source-id");
        this.sourceId = StrUtil.fillAfter(this.sourceId.trim(), ' ', 11);
        List<Map> fields = (List<Map>) messageMap.get("fields");
        this.fieldIndexMap = new HashMap<>();
        this.fieldNameMap = new HashMap<>();
        String name;
        for (Map field : fields) {
            Object o = field.get("index");
            if (o == null) {
                throw new BizException(BizResultEnum.CONVERTOR_ISO8583_COFIG_ERROR);
            }
            int index = (int) field.get("index");
            o = field.get("name");

            if (o == null) {
                name = "f" + index;
            } else {
                name = (String) o;
            }
            this.fieldIndexMap.put(index, field);
            this.fieldNameMap.put(name, field);
        }
    }

    @Override
    protected JSONObject biz2json(JSONObject inMessage) throws BizException {
        return inMessage;
    }

    @Override
    protected byte[] json2adaptor(JSONObject inMessage) throws BizException {
        String bitMapAndMsgStr = getBitMapAndMsg(inMessage, 128);
        int bitMapAndMsgLen = bitMapAndMsgStr.length();
        int rspTotLen = 46 + 4 + bitMapAndMsgLen;

        String head = (String) inMessage.get(this.getMessageHeadFieldName());
        if (head == null) {
            throw new BizException(BizResultEnum.CONVERTOR_ISO8583_PACK_ERROR, "msgHead为空");
        }

        byte[] rspHead = HexUtil.decodeHex(head);
        System.arraycopy(String.format("%04d", rspTotLen).getBytes(), 0, rspHead, 2, 4);
        System.arraycopy(this.destinationId.getBytes(), 0, rspHead, 6, 11);
        System.arraycopy(this.sourceId.getBytes(), 0, rspHead, 17, 11);
//        iso8583DTO128.setMsgHead(rspHead);

        StringBuilder sendMsg = new StringBuilder();
        // 报文总长度
//        sendMsg.append(String.format("%04d", rspTotLen));
        // 8583消息头
        sendMsg.append(new String(rspHead, StandardCharsets.ISO_8859_1));
        // 8583消息类型
        String messageType = (String) inMessage.get(this.getMessageTypeFieldName());
        if (messageType == null) {
            throw new BizException(BizResultEnum.CONVERTOR_ISO8583_PACK_ERROR, "msgType为空");
        }
        sendMsg.append(messageType);
        // 8583位图和消息
        sendMsg.append(bitMapAndMsgStr);
        return sendMsg.toString().getBytes(StandardCharsets.ISO_8859_1);
    }

    @Override
    protected JSONObject adaptor2json(byte[] receivedMsgBytes) throws BizException {
        //去掉前面的长度
        int totalLen = receivedMsgBytes.length;
//        receivedMsgBytes = ByteBitUtil.subBytes(receivedMsgBytes,4,totalLen-4);
//        totalLen =totalLen-4;
        if (totalLen < 56) {
            System.out.println("报文格式不正确，报文长度最少为56");
            return null;
        }
        byte[] head = ByteBitUtil.subBytes(receivedMsgBytes, 0, 46);
        // printHead(head);

        String messageType = new String(ByteBitUtil.subBytes(receivedMsgBytes, 46, 4));
        System.out.println("MsgType = [" + messageType + "]");

        BitSet bitMap = ByteBitUtil.byteArray2BitSet(ByteBitUtil.subBytes(receivedMsgBytes,50,1));
        byte[] msgBytes;
        if (bitMap.get(0)) {
            bitMap = ByteBitUtil.byteArray2BitSet(ByteBitUtil.subBytes(receivedMsgBytes, 50, 16));
            msgBytes = ByteBitUtil.subBytes(receivedMsgBytes, 46 + 16 + 4, totalLen - (46 + 16 + 4));
        }
        else {
            bitMap = ByteBitUtil.byteArray2BitSet(ByteBitUtil.subBytes(receivedMsgBytes, 50, 8));
            msgBytes = ByteBitUtil.subBytes(receivedMsgBytes, 46 + 8 + 4, totalLen - (46 + 8 + 4));
        }
        JSONObject jsonObject = msgToObject128(bitMap, msgBytes);

        jsonObject.set(this.getMessageTypeFieldName(), messageType);
        jsonObject.set(this.getMessageHeadFieldName(), HexUtil.encodeHexStr(head));
        return jsonObject;
    }

    @Override
    protected JSONObject json2biz(JSONObject inMessage) throws BizException {
        return inMessage;
    }


    private JSONObject msgToObject128(BitSet bitMap, byte[] msg) throws BizException {

        // 返回对象
//            Object retObject = clazz.newInstance();
        // 获取ISO8583DTO类的属性，key为fldIndex域序号，value为属性名
//            Map<Integer, String> iso8583DTOFldMap = getISO8583DTOFldMap(clazz);
        int indexFlag = 0;
        String fldName;
        int fldUnfixed;
        int fldLength;
//            Field field;
//            ISO8583Annotation fldAnnotation;
//            FldFlag fldFlag;
        int dataLength;
        String fldValue;
        JSONObject jsonObject = new JSONObject();
        // 从位图第2位置开始
        for (int i = 1; i < bitMap.length(); i++) {
            if (!bitMap.get(i)) {
                continue;
            }
            // 位图下标从1开始，所以需要+1
            Map fieldDefMap = this.fieldIndexMap.get(i + 1);
            fldName = (String) fieldDefMap.get("name");
            if (fldName == null) {
                fldName = "f" + String.valueOf(i + 1);
            }
            Object o = fieldDefMap.get("unfixed");
            if (o == null) {
                fldUnfixed = 0;
            } else {
                fldUnfixed = (int) o;
            }
            o = fieldDefMap.get("length");
            if (o == null) {
                fldLength = 0;
            } else {
                fldLength = (int) o;
            }
//                field = clazz.getDeclaredField(fldName);
//                fldAnnotation = field.getAnnotation(ISO8583Annotation.class);
//                fldFlag = fldAnnotation.fldFlag();
            if (fldUnfixed == 0) {
                dataLength = fldLength;
            } else if (fldUnfixed == 2) {
                dataLength = Integer.parseInt(new String(ByteBitUtil.subBytes(msg, indexFlag, 2)));
                indexFlag = indexFlag + 2;
            } else if (fldUnfixed == 3) {
                dataLength = Integer.parseInt(new String(ByteBitUtil.subBytes(msg, indexFlag, 3)));
                indexFlag = indexFlag + 3;
            } else {
                // 未知类型，不做处理
                continue;
            }
            try {
                fldValue = new String(ByteBitUtil.subBytes(msg, indexFlag, dataLength), CharsetUtil.ISO_8859_1);
            } catch (UnsupportedEncodingException e) {
                throw new BizException(BizResultEnum.CONVERTOR_UNSUPPORTED_ENCODE_ERROR, e);
            }
            indexFlag += dataLength;
            jsonObject.set(fldName, fldValue);
//                field.setAccessible(true);
//                field.set(retObject, fldValue);
            log.trace("Field " + (i + 1) + "= [" + fldValue + "]");
        }

        return jsonObject;


    }

    private String getBitMapAndMsg(JSONObject jsonObject, int bitLen) throws BizException {
        // 获取ISO8583DTO类的属性，key为fldIndex域序号，value为属性名
//        Map<Integer, String> iso8583DTOFldMap = getISO8583DTOFldMap(iso8583DTO.getClass());
        // 初始化域位图
        BitSet bitMap = new BitSet();
        for (int i = 0; i < 128; i++) {
            bitMap.set(i, false);
        }
        bitMap.set(0, false);

        // 获取有值的域，并生成位图
//        PropertyDescriptor propertyDescriptor;
        String fldValue;
        int index;
        // 按照格式处理后的值
        String fldSendValue;
        // 将每个域对应的值，保存到对应下标中
        String[] fldSendValues = new String[bitLen];
//        try {
        // 循环判断哪个字段有值
        for (String fldName : jsonObject.keySet()) {
//                fldName = entry.getValue();
//                propertyDescriptor = new PropertyDescriptor(fldName, iso8583DTO.getClass());
            Map field = this.fieldNameMap.get(fldName);
            if (field == null) {
                continue;
            }
            fldValue = jsonObject.getStr(fldName);

            index = (int) field.get("index");
            if (StrUtil.isNotEmpty(fldValue)) {
                // 如果此域有值，将对应的位图位置修改为1
                bitMap.set(index - 1, true);
                // 根据注解对值进行处理
//                    Field field = iso8583DTO.getClass().getDeclaredField(fldName);
                fldSendValue = verifyAndTransValue(field, fldValue);
                fldSendValues[index - 1] = fldSendValue;
                if (index >= 66) {
                    bitMap.set(0,true);
                }
            }
        }

        // 将128位bitmap转换为16byte
        byte[] bitMapBytes = ByteBitUtil.bitSet2ByteArray(bitMap);
        String bitMapStr = null;
        try {
            bitMapStr = new String(bitMapBytes, CharsetUtil.ISO_8859_1);
        } catch (UnsupportedEncodingException e) {
            throw new BizException(BizResultEnum.CONVERTOR_UNSUPPORTED_ENCODE_ERROR, e);
        }
        StringBuilder bitMapAndMsg = new StringBuilder();
        // 位图在前，先拼接位图
        bitMapAndMsg.append(bitMapStr);
        // 拼接报文数据
        for (String value : fldSendValues) {
            if (StringUtils.isNotEmpty(value)) {
                bitMapAndMsg.append(value);
            }
        }

        return bitMapAndMsg.toString();
    }

    /**
     * 组包时根据字段原值按照其配置规则转为十六进制 PACK
     */
    private String verifyAndTransValue(Map fieldDefMap, String fldValue) throws BizException {
        int index = (Integer) fieldDefMap.get("index");
        String fldName = this.getFieldName(index);
        int fldUnfixed, fldLength;
        Object o = fieldDefMap.get("unfixed");
        if (o == null) {
            fldUnfixed = 0;
        } else {
            fldUnfixed = (int) o;
        }
        o = fieldDefMap.get("length");
        if (o == null) {
            fldLength = 0;
        } else {
            fldLength = (int) o;
        }

        int actualLen = fldValue.length();

        // 固定长度，则校验一下长度是否一致
        if (fldUnfixed == 0) {
            if (actualLen != fldLength) {
                String msg = String.format("%s长度不正确，期望长度为[%d]，实际长度为[%d]。", fldName, fldLength, actualLen);
                log.error(msg);
                throw new BizException(BizResultEnum.CONVERTOR_ISO8583_PACK_ERROR, msg);
            }
            return fldValue;
        }

        // 可变长度，校验一下长度是否超过上限。如果长度符合，则在前边拼接长度值
        if (fldUnfixed == 2 || fldUnfixed == 3) {
            if (actualLen > fldLength) {
                String msg = String.format("%s长度不正确，最大长度为[%d]，实际长度为[%d]。", fldName, fldLength, actualLen);
                throw new BizException(BizResultEnum.CONVERTOR_ISO8583_PACK_ERROR, msg);
            }
            int len = 2;
            if (fldUnfixed == 3) {
                len = 3;
            }
            // 在报文前边拼接长度
            fldValue = StringUtils.leftPad(String.valueOf(actualLen), len, "0") + fldValue;
            return fldValue;
        }
        return fldValue;
    }

    private String getMessageHeadFieldName() {
        if (this.fieldIndexMap.get(0) == null) {
            return "msgHead";
        } else {
            Map fieldDefMap = this.fieldIndexMap.get(0);
            return (String) fieldDefMap.get("name");
        }
    }

    private String getMessageTypeFieldName() {
        if (this.fieldIndexMap.get(1) == null) {
            return "msgType";
        } else {
            Map fieldDefMap = this.fieldIndexMap.get(1);
            return (String) fieldDefMap.get("name");
        }
    }

    private String getFieldName(int index) throws BizException {
        Map fieldMap = this.fieldIndexMap.get(index);
        if (fieldMap == null) {
            throw new BizException(BizResultEnum.CONVERTOR_ISO8583_COFIG_ERROR);
        }
        String name = (String) fieldMap.get("name");
        if (name == null) {
            return "f" + index;
        }
        return name;
    }
}
